package com.swak.mongo;

import java.util.List;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.util.Assert;

import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import com.swak.entity.Page;
import com.swak.entity.Parameters;
import com.swak.mongo.json.Document;
import com.swak.mongo.json.Query;
import com.swak.mongo.json.Update;
import com.swak.utils.Lists;

/**
 * 客户端
 * 
 * @author lifeng
 */
public class MongoSyncOptions implements DisposableBean {

	private final com.mongodb.client.MongoClient mongoClient;
	private final com.mongodb.client.MongoDatabase mongoDB;

	public MongoSyncOptions(com.mongodb.client.MongoClient mongoClient, String dbName) {
		this.mongoClient = mongoClient;
		this.mongoDB = mongoClient.getDatabase(dbName);
	}

	/**
	 * 根据ID获取数据
	 * 
	 * @param table
	 * @param id
	 * @return
	 */
	public Document get(String table, Object id) {
		Assert.notNull(table, "table can not null");
		Assert.notNull(id, "id can not null");
		MongoCollection<Document> collection = this.mongoDB.getCollection(table, Document.class);
		Document query = new Document();
		query.put(Document.ID_FIELD, id);
		return collection.find(query).first();
	}

	/**
	 * 根据ID获取数据
	 * 
	 * @param table
	 * @param id
	 * @return
	 */
	public Document get(String table, Query query) {
		Assert.notNull(table, "table can not null");
		Assert.notNull(query, "id can not null");
		MongoCollection<Document> collection = this.mongoDB.getCollection(table, Document.class);
		return collection.find(query.getFilter()).first();
	}

	/**
	 * 根据ID获取数据
	 * 
	 * @param table
	 * @param id
	 * @return
	 */
	public Integer count(String table, Query query) {
		Assert.notNull(table, "table can not null");
		Assert.notNull(query, "query can not null");
		MongoCollection<Document> collection = this.mongoDB.getCollection(table, Document.class);
		Long count = collection.countDocuments(query.getFilter());
		return count.intValue();
	}

	/**
	 * 根据ID获取数据
	 * 
	 * @param table
	 * @param id
	 * @return
	 */
	public Page<Document> page(String table, Query query, Parameters param) {
		Assert.notNull(table, "table can not null");
		Assert.notNull(query, "query can not null");
		MongoCollection<Document> collection = this.mongoDB.getCollection(table, Document.class);
		Long count = collection.countDocuments(query.getFilter());
		param.setRecordCount(count.intValue());
		List<Document> docs = _query(collection, query, param);
		return new Page<Document>(param, docs);
	}

	private List<Document> _query(MongoCollection<Document> collection, Query $query, Parameters param) {
		FindIterable<Document> find = collection.find(Document.class);
		find.filter($query.getFilter()).projection($query.getFields()).limit(param.getPageSize())
				.skip((param.getPageIndex() - 1) * param.getPageSize()).sort($query.getOrder());
		List<Document> results = Lists.newArrayList();
		find.into(results);
		return results;
	}

	/**
	 * 根据ID获取数据
	 * 
	 * @param table
	 * @param id
	 * @return
	 */
	public List<Document> query(String table, Query query, int limit) {
		Assert.notNull(table, "table can not null");
		Assert.notNull(query, "query can not null");
		MongoCollection<Document> collection = this.mongoDB.getCollection(table, Document.class);
		FindIterable<Document> find = collection.find(Document.class);
		find.limit(limit).sort(query.getOrder()).filter(query.getFilter()).projection(query.getFields());
		List<Document> results = Lists.newArrayList();
		find.into(results);
		return results;
	}

	/**
	 * 插入数据
	 * 
	 * @param table
	 * @param docs
	 * @return
	 */
	public void insert(String table, Document... docs) {
		Assert.notNull(table, "table can not null");
		Assert.notNull(docs, "docs can not null");
		Assert.notEmpty(docs, "docs can not empty");
		MongoCollection<Document> collection = this.mongoDB.getCollection(table, Document.class);
		if (docs.length == 1) {
			collection.insertOne(docs[0]);
		} else {
			collection.insertMany(Lists.newArrayList(docs));
		}
	}

	/**
	 * 保存数据
	 * 
	 * @param table
	 * @param doc
	 * @return
	 */
	public void save(String table, Document doc) {
		Assert.notNull(table, "table can not null");
		Assert.notNull(doc, "doc can not null");
		MongoCollection<Document> collection = this.mongoDB.getCollection(table, Document.class);
		Object id = doc.get(Document.ID_FIELD);
		if (id == null) {
			collection.insertOne(doc);
			return;
		} else {
			com.mongodb.client.model.UpdateOptions options = new com.mongodb.client.model.UpdateOptions().upsert(true);
			Document filter = new Document();
			filter.put(Document.ID_FIELD, id);
			collection.updateOne(filter, new Update().set(doc), options);
			return;
		}
	}

	/**
	 * 修改数据
	 * 
	 * @param table
	 * @param doc
	 * @return
	 */
	public Long update(String table, Document doc, Update update) {
		Assert.notNull(table, "table can not null");
		Assert.notNull(doc, "doc can not null");
		MongoCollection<Document> collection = this.mongoDB.getCollection(table, Document.class);
		com.mongodb.client.model.UpdateOptions options = new com.mongodb.client.model.UpdateOptions().upsert(true);
		Document filter = new Document();
		filter.put(Document.ID_FIELD, doc.get(Document.ID_FIELD));
		UpdateResult result = collection.updateOne(filter, update, options);
		return result.getModifiedCount();
	}

	/**
	 * 删除数据
	 * 
	 * @param table
	 * @param doc
	 * @return
	 */
	public Long delete(String table, Document doc) {
		Assert.notNull(table, "table can not null");
		Assert.notNull(doc, "doc can not null");
		MongoCollection<Document> collection = this.mongoDB.getCollection(table, Document.class);
		Object id = doc.get(Document.ID_FIELD);
		DeleteResult result = id == null ? collection.deleteOne(doc) : collection.deleteMany(doc);
		return result.getDeletedCount();
	}

	/**
	 * Mongo Holder
	 * 
	 * @author lifeng
	 */
	public static class MongoDataSource {

		public String dbName;
		public com.mongodb.client.MongoClient mongoClient;
		public com.mongodb.client.MongoDatabase mongoDB;

		public MongoDataSource(com.mongodb.client.MongoClient mongoClient, String dbName) {
			this.mongoClient = mongoClient;
			this.dbName = dbName;
			this.mongoDB = mongoClient.getDatabase(dbName);
		}
	}

	@Override
	public void destroy() throws Exception {
		try {
			this.mongoClient.close();
		} catch (Exception e) {
		}
	}
}
